import java.util.ArrayList;

public class Behavior {

	int population = 100;
	double initFreqCoop = 0.5;
	double mortality = 10;
	double cost = 5;
	int benefit = 10;
	int diedThisRound = 0;
	int shotCount;
	StrategyType a = StrategyType.cooperator;

	ArrayList<Agent> agentList = new ArrayList<Agent>();

	public Behavior(int population, double initFreqCoop, double cost,
			int benefit, double mortality, int shotCount) {
		this.population = population;
		this.initFreqCoop = initFreqCoop;
		this.cost = cost;
		this.benefit = benefit;
		this.mortality = mortality;
		this.shotCount = shotCount;
		
		inizialize();
	}

	private void inizialize() {

		int tempDiv = (int) (this.population * this.initFreqCoop);

		// Constructing Agent objects, with different attributes, and adding
		// them to the List.
		for (int i = 0; i < tempDiv; i++)
			agentList.add(new Agent(StrategyType.cooperator));

		for (int i = tempDiv; i < (this.population); i++)
			agentList.add(new Agent(StrategyType.defector));
	}

	public void interactPrisoners(double groupSize, double benefitThreshold) {
		// Agents interact in the prisoners dilemma game.
		double repetition = agentList.size() / groupSize;

		for (int u = 0; u < repetition; u++) {
			ArrayList<Agent> l = new ArrayList<Agent>();

			for (int i = 0; i < groupSize; i++) {
				l.add(getRandomAgent());
			}

			// Calculates whether the the group is granted a reward.
			int coopNum = 0;
			for (Agent a : l) {
				if (a.getStragedy() == StrategyType.cooperator) {
					a.pay(cost);
					coopNum++;
				}
			}

			// Agents receive their reward.
			if (coopNum >= benefitThreshold) {
				reward(l);
			}
		}
	}

	public void interactSnowDrift(double groupSize, double benefitThreshold) {
		// Agents interact in the prisoners dilemma game.
		double repetition = agentList.size() / groupSize;

		for (int u = 0; u < repetition; u++) {
			ArrayList<Agent> l = new ArrayList<Agent>();

			for (int i = 0; i < groupSize; i++) {
				l.add(getRandomAgent());
			}

			// Interaction.
			for (int i = 0; i <= shotCount; i++) {
				// Calculates whether the the group is granted a reward.
				int coopNum = 0;
				for (Agent a : l) {
					if (a.getStragedy() == StrategyType.cooperator) {
						coopNum++;
					}
				}
				
				// Calculates what cost should be payed by the cooperators.
				double tempCost = 0;
				if (coopNum >= benefitThreshold) {
					reward(l);
					tempCost = cost / coopNum;
				}

				// Cooperators pay.
				for (Agent a : l) {
					if (a.getStragedy() == StrategyType.cooperator) {
						a.pay(tempCost);
					}
				}
			}
		}
	}
	
	public void mortality() {
		int repetition = (int) Math.round((double) agentList.size()
				* (double) mortality / 100);
		for (int i = 0; i < repetition; i++) {
			agentList.remove(getRandomAgent());
			population--;
			diedThisRound++;
		}
	}

	public void reproduction() {
		// Calculates averages.
		double coopFitness = 0;
		double defFitness = 0;
		int coopNum = 0;
		int defNum = 0;

		for (Agent a : agentList) {

			if (a.getStragedy() == StrategyType.cooperator) {
				coopNum++;
				coopFitness += a.getPayoff();
			} else if (a.getStragedy() == StrategyType.defector) {
				defNum++;
				defFitness += a.getPayoff();
			}
		}

		double avgDef = defFitness / (double) defNum;
		double avgCoop = coopFitness / (double) coopNum;
		double size = (double) agentList.size();
		double fitness = (avgCoop * (coopNum / size))
				/ ((avgCoop * (coopNum / size)) + (avgDef * (defNum / size)));

		for (int i = 0; i < diedThisRound; i++) {
			// Inserts Agent in relation to the above calculation.
			if (Math.random() <= fitness) {
				agentList.add(new Agent(StrategyType.cooperator));
			} else {
				agentList.add(new Agent(StrategyType.defector));
			}
			population++;
		}

		// Resets payoff for next generation.
		if (diedThisRound != 0) {
			for (Agent a : agentList) {
				a.setPayoff(0);
			}
			diedThisRound = 0;
		}
	}

	public void printAll() {
		int coopNum = 0;
		int defNum = 0;
		for (Agent a : agentList) {

			if (a.getStragedy() == StrategyType.cooperator) {
				coopNum++;
			} else if (a.getStragedy() == StrategyType.defector) {
				defNum++;
			}
		}
		System.out.println("Population size: " + agentList.size());
		System.out.println("Number of cooperators: " + coopNum
				+ "  |  Number of defectors: " + defNum);
		System.out.println("Percentage of cooperators: " + 100
				* (double) coopNum / agentList.size());
		System.out.println("");
	}

	public void printToData() {
		int coopNum = 0;
		for (Agent a : agentList) {

			if (a.getStragedy() == StrategyType.cooperator) {
				coopNum++;
			}
		}
		System.out.println((double) coopNum / agentList.size());
	}
	
	public double getCoopRatio() {
			int coopNum = 0;
			for (Agent a : agentList) {
	
				if (a.getStragedy() == StrategyType.cooperator) {
					coopNum++;
				}
			}
			return ((double) coopNum / agentList.size());
	}

	private Agent getRandomAgent() {
		return agentList.get((int) (agentList.size() * Math.random()));
	}

	private void reward(ArrayList<Agent> agentList) {
		for (Agent a : agentList) {
			a.setPayoff(benefit);
		}
	}
}